//page alloc
// $Id: alloc_page.cpp 568 2010-10-25 15:41:07Z Oleg.Bulychov $

#include "stdafx.h"

#include "src/heo/include/sys_alloc.h"
#include "src/heo/include/alloc_page.h"

//__________________________________________________________________________________
namespace mem_util
{
//__________________________________________________________________________________
//__________________________________________________________________________________
class page_allocator_impl
{
    DISALLOW_COPY_AND_ASSIGN(page_allocator_impl);
    typedef sys_page_allocator::TPage TPage;
    sys_page_allocator sys_alloc_;
    TPage* first_free_page_;
    size_t free_pages_;
    TPage* commit_page_;
    enum {commit_step = 32};
public:
    explicit page_allocator_impl() NO_THROW_():
        first_free_page_(),
        free_pages_(),
        commit_page_()
    {}
//.............................................................................
    int construct(size_t mb) NO_THROW_()
    {
        mb *= 1024 * 1024;
        size_t max_pages = mb / MEM_PAGE_SIZE;
        if (mb % MEM_PAGE_SIZE)
            ++max_pages;
        if (!sys_alloc_.Alloc(max_pages))
            return 0;
        first_free_page_ = sys_alloc_.get_Buffer();
        commit_page_ = first_free_page_;
        free_pages_ = max_pages;
        return 1;
    }
//.............................................................................
    void* Alloc() NO_THROW_()
    {
        if (!free_pages_)
            return NULL;
        --free_pages_;
        TPage** p = (TPage**)first_free_page_;
        if (first_free_page_ == commit_page_)
            commit();
        first_free_page_ = *p;
        return p;
    }
//.............................................................................
    void Free(void* pPage) NO_THROW_()
    {
        if (!pPage)
            return;
        ASSERT_(ROUND2(size_t(pPage), MEM_PAGE_SIZE) == (size_t)pPage);
        ASSERT_(pPage >= sys_alloc_.get_Buffer());
        ASSERT_(pPage < sys_alloc_.get_Buffer() + sys_alloc_.get_Num_Pages());
        TPage** p = (TPage**)pPage;
        *p = first_free_page_;
        first_free_page_ = (TPage*)pPage;
        ++free_pages_;
    }
//.............................................................................
private:
    void commit() NO_THROW_()
    {
        TPage* commit_end = commit_page_ + std::min(size_t(commit_step),
            free_pages_);
        for (; commit_page_ < commit_end;)
        {
            TPage** p = (TPage**)commit_page_;
            *p = ++commit_page_;//:touch page p
        }
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
}//:mem_util
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
static mem_util::page_allocator_impl* g_impl = NULL;
static int g_count = 0;
//.............................................................................
bool page_allocator::construct(int nMaxThreads, size_t mb) NO_THROW_()
{
    using namespace mem_util;
    g_impl = (page_allocator_impl*)malloc(sizeof(page_allocator_impl) * nMaxThreads);
    if (!g_impl)
        return false;
    g_count = nMaxThreads;
    for (int i = 0; i < nMaxThreads; ++i)
    {
        new(&g_impl[i]) page_allocator_impl;
        if (!g_impl[i].construct(mb))
            return false;
    }
    return true;
}
//.............................................................................
void page_allocator::destruct() NO_THROW_()
{
    using namespace mem_util;
    for (int i = 0; i < g_count; ++i)
        g_impl[i].~page_allocator_impl();
    free(g_impl);
    g_impl = NULL;
    g_count = 0;
}
//.............................................................................
//.............................................................................
void* page_allocator::Alloc(int nRank) NO_THROW_()
{
    ASSERT_(nRank >= 0);
    ASSERT_(nRank < g_count);
    return g_impl[nRank].Alloc();
}
//.............................................................................
void page_allocator::Free(int nRank, void* ptr) NO_THROW_()
{
    ASSERT_(nRank >= 0);
    ASSERT_(nRank < g_count);
    g_impl[nRank].Free(ptr);
}
//.............................................................................
//.............................................................................
//.............................................................................
//.............................................................................
//.............................................................................

//EOF!
